# -*- coding: utf-8 -*-
# @Time    : 2024/6/18 16:58
# @Author  : yujiahao
# @File    : 30_class_reflex_special_methods.py
# @description:反射和双下方法


# todo 一.反射

'''
反射的概念是由Smith在1982年首次提出的，主要是指程序可以访问、检测和修改它本身状态或行为的一种能力（自省）。
这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。

python面向对象中的反射：通过字符串的形式操作对象相关的属性。python中的一切事物都是对象（都可以使用反射）
四个可以实现自省的函数
下列方法适用于类和对象（一切皆对象，类本身也是一个对象）
'''


# 1.1 对象的反射
class Foo:
    f = '类的静态变量'

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def say_hi(self):
        print('hi,%s' % self.name)


obj = Foo('wawu', 73)

# 检测是否含有某属性和某方法
print(hasattr(obj, 'name'))
print(hasattr(obj, 'say_hi'))

# 获取属性
n = getattr(obj, 'name')
print(n)
func = getattr(obj, 'say_hi')
func()

print(getattr(obj, 'aaaaaaaa', '如果不存在则返回这个默认值'))

# 设置属性
setattr(obj, 'add_newattr', '添加对应的值')
setattr(obj, 'show_name', lambda self: self.add_newattr + '\tadd_newattr')
print(obj.__dict__)
# 调用新添加的方法和属性
print(obj.show_name(obj))

# 删除属性和方法
delattr(obj, 'age')
delattr(obj, 'show_name')
try:
    delattr(obj, 'show_name111')  # 不存在,则报错
except Exception as e:
    print("你想删除的东西不存在")
print(obj.__dict__)

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')


# 1.2 对类的反射
class Foo(object):
    staticField = "old boy"

    def __init__(self):
        self.name = 'wupeiqi'

    def func(self):
        return 'func'

    @staticmethod
    def bar():
        return 'bar'


print(getattr(Foo, 'staticField'))

print(getattr(Foo, 'func'))

print(getattr(Foo, 'bar'))

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# 1.3 当前模块的反射
import sys


def s1():
    print('s1')


def s2():
    print('s2')


# 使用 sys.modules 字典来获取当前模块的引用。__name__ 是当前模块的名称，通常如果是主模块则为 '__main__'。
this_module = sys.modules[__name__]
# 函数检查 this_module（当前模块）是否有一个名为 s1 的属性（在这种情况下是函数 s1）。这应该返回 True
print(hasattr(this_module, 's1'))
getattr(this_module, 's2')()

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# 1.4 其他模块的反射

'''
# 一个模块中的代码
    def test():
        print('from the test')


 
程序目录：
    module_test.py
    index.py


当前文件：
    index.py
 


# 另一个模块中的代码
    import module_test as obj
    
    # obj.test()
    
    print(hasattr(obj, 'test'))
    
    getattr(obj, 'test')()

'''

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# 1.5 反射的应用

'''
了解了反射的四个函数。那么反射到底有什么用呢？它的应用场景是什么呢？

现在让我们打开浏览器，访问一个网站，你单击登录就跳转到登录界面，你单击注册就跳转到注册界面，等等，其实你单击的其实是一个个的链接，
每一个链接都会有一个函数或者方法来处理。


'''


# 没学反射之前的解决方式
class User:
    def login(self):
        print('欢迎来到登录页面')

    def register(self):
        print('欢迎来到注册页面')

    def save(self):
        print('欢迎来到存储页面')


while True:
    choose = input('>>>').strip()
    if choose == 'exit':
        print('退出程序')
        break  # 退出循环
    elif choose == 'login':
        obj = User()
        obj.login()
    elif choose == 'register':
        obj = User()
        obj.register()
    elif choose == 'save':
        obj = User()
        obj.save()
    else:
        print('输入错误，请重新输入')


# 学了反射之后解决方式

class User:
    def login(self):
        print('欢迎来到登录页面')

    def register(self):
        print('欢迎来到注册页面')

    def save(self):
        print('欢迎来到存储页面')


user = User()
while True:
    choose = input('>>>').strip()
    if choose == 'exit':
        print('退出程序')
        break  # 退出循环
    elif hasattr(user, choose):
        func = getattr(user, choose)
        func()
    else:
        print('输入错误，请重新输入')

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')

# todo 二. 双下方法

'''
定义：Python 中的双下方法（通常称为魔术方法或特殊方法）是一系列预定义的、使用双下划线开头和结尾的方法，它们具有特殊的意义和用途。
这些方法通常不需要直接调用，而是通过特定的 Python 语法（如运算符或内置函数）自动触发

双下方法主要是python源码程序员使用的，我们在开发中尽量不要使用双下方法（不要滥用或者误用），但是深入研究双下方法，更有益于我们阅读源码。

    class B:
        def __len__(self):
            print(666)
    
    b = B()
    # len(b) # len 一个对象就会触发 __len__方法。会报错
    b.__len__()
    
    class A:
        def __init__(self):
            self.a = 1
            self.b = 2
    
        def __len__(self):
            return len(self.__dict__)
    a = A()
    print(len(a))

调用：不同的双下方法有不同的触发方式，就好比盗墓时触发的机关一样，不知不觉就触发了双下方法，例如：init
'''


class A:
    def __new__(cls, a, b):
        """
        创建并返回一个新的实例。

        该方法在 __init__ 之前调用，用于控制实例的创建过程。

        :param a: 初始化属性 a 的值
        :param b: 初始化属性 b 的值
        :return: 新的实例
        """
        instance = super(A, cls).__new__(cls)
        return instance

    def __init__(self, a, b):
        """
        初始化方法，设置实例的属性 a 和 b。

        :param a: 初始化属性 a 的值
        :param b: 初始化属性 b 的值
        """
        self.a = a
        self.b = b

    def __del__(self):
        """
        析构方法，在对象被垃圾回收时调用。

        用于清理资源或执行其他必要的清理操作。
        """
        print(f"Deleting instance with a={self.a} and b={self.b}")

    def __call__(self, *args, **kwargs):
        """
        使得类的实例可以像函数一样被调用。

        :param args: 位置参数
        :param kwargs: 关键字参数
        :return: 返回一个字符串，表示调用了该实例

        对象后面加括号，触发执行。

        注：构造方法new的执行是由创建对象触发的，即：对象 = 类名() ；而对于 call 方法的执行是由对象后加括号触发的，即：对象() 或者 类()()
        """
        return f"Called with args: {args} and kwargs: {kwargs}"

    def __hash__(self):
        """
        计算对象的哈希值。

        将属性 a 和 b 转换为字符串并连接起来，然后计算其哈希值。
        这样可以确保对象在属性 a 和 b 不变的情况下，哈希值保持一致。

        :return: 对象的哈希值
        """
        return hash((self.a, self.b))

    def __eq__(self, other):
        """
        比较两个对象是否相等。

        如果两个对象的类型相同且属性 a 和 b 的值相等，则认为它们相等。
        调用方式 a == b
        :param other: 要比较的另一个对象
        :return: 如果两个对象相等则返回 True，否则返回 False
        """
        if isinstance(other, A):
            return self.a == other.a and self.b == other.b
        return False

    def __ne__(self, other):
        """
        比较两个对象是否不相等。

        通过调用 __eq__ 方法并取反来实现。

        :param other: 要比较的另一个对象
        :return: 如果两个对象不相等则返回 True，否则返回 False
        """
        return not self.__eq__(other)

    def __str__(self):
        """
        返回对象的字符串表示。

        该方法用于 print 函数或 str() 函数调用时。

        :return: 对象的字符串表示


        __str__ 返回的字符串是 A(a=1, b=2)，它是一个用户友好的表示，便于阅读。
        __repr__ 返回的字符串是 A(1, 2)，它是一个正式的表示，包含了对象的类型和构造参数，便于调试和开发。
        """
        return f"A(a={self.a}, b={self.b})"

    def __repr__(self):
        """
        返回对象的正式字符串表示。

        该方法用于调试和交互解释器中。

        :return: 对象的正式字符串表示,如果一个类中定义了repr方法，那么在repr(对象) 时，默认输出该方法的返回值。
        """
        return f"A(a={self.a}, b={self.b})"

    def __lt__(self, other):
        """
        比较两个对象的大小（小于）。

        通过比较属性 a 和 b 来实现。

        :param other: 要比较的另一个对象
        :return: 如果当前对象小于其他对象则返回 True，否则返回 False
        """
        if isinstance(other, A):
            return (self.a, self.b) < (other.a, other.b)
        return NotImplemented

    def __le__(self, other):
        """
        比较两个对象的大小（小于等于）。

        通过比较属性 a 和 b 来实现。

        :param other: 要比较的另一个对象
        :return: 如果当前对象小于或等于其他对象则返回 True，否则返回 False
        """
        if isinstance(other, A):
            return (self.a, self.b) <= (other.a, other.b)
        return NotImplemented

    def __gt__(self, other):
        """
        比较两个对象的大小（大于）。

        通过比较属性 a 和 b 来实现。

        :param other: 要比较的另一个对象
        :return: 如果当前对象大于其他对象则返回 True，否则返回 False
        """
        if isinstance(other, A):
            return (self.a, self.b) > (other.a, other.b)
        return NotImplemented

    def __ge__(self, other):
        """
        比较两个对象的大小（大于等于）。

        通过比较属性 a 和 b 来实现。

        :param other: 要比较的另一个对象
        :return: 如果当前对象大于或等于其他对象则返回 True，否则返回 False
        """
        if isinstance(other, A):
            return (self.a, self.b) >= (other.a, other.b)
        return NotImplemented

    def __add__(self, other):
        """
        实现两个对象相加。

        通过将两个对象的属性 a 和 b 分别相加来实现。

        :param other: 要相加的另一个对象
        :return: 返回一个新的对象，其属性 a 和 b 是两个对象相加的结果
        """
        if isinstance(other, A):
            return A(self.a + other.a, self.b + other.b)
        return NotImplemented

    def __sub__(self, other):
        """
        实现两个对象相减。

        通过将两个对象的属性 a 和 b 分别相减来实现。

        :param other: 要相减的另一个对象
        :return: 返回一个新的对象，其属性 a 和 b 是两个对象相减的结果
        """
        if isinstance(other, A):
            return A(self.a - other.a, self.b - other.b)
        return NotImplemented

    def __len__(self):
        """
        返回对象的长度。

        这里假设长度为属性 a 和 b 的和。

        :return: 对象的长度
        """
        return self.a + self.b


# 创建实例
a1 = A(1, 2)
a2 = A(1, 2)
a3 = A(2, 3)

# 打印哈希值
print(f"调用 __hash__ 方法: hash(a1) = {hash(a1)}")  # 输出: 与 a2 相同的哈希值
print(f"调用 __hash__ 方法: hash(a2) = {hash(a2)}")  # 输出: 与 a1 相同的哈希值
print(f"调用 __hash__ 方法: hash(a3) = {hash(a3)}")  # 输出: 不同于 a1 和 a2 的哈希值

# 检查相等性
print(f"调用 __eq__ 方法: a1 == a2: {a1 == a2}")  # 输出: True
print(f"调用 __eq__ 方法: a1 == a3: {a1 == a3}")  # 输出: False

# 检查不相等性
print(f"调用 __ne__ 方法: a1 != a2: {a1 != a2}")  # 输出: False
print(f"调用 __ne__ 方法: a1 != a3: {a1 != a3}")  # 输出: True

# 打印字符串表示
print(f"调用 __str__ 方法: str(a1) = {str(a1)}")  # 输出: A(a=1, b=2)

# 打印正式字符串表示
print(f"调用 __repr__ 方法: repr(a1) = {repr(a1)}")  # 输出: A(a=1, b=2)

# 比较大小
print(f"调用 __lt__ 方法: a1 < a3: {a1 < a3}")  # 输出: True
print(f"调用 __le__ 方法: a1 <= a2: {a1 <= a2}")  # 输出: True
print(f"调用 __gt__ 方法: a3 > a1: {a3 > a1}")  # 输出: True
print(f"调用 __ge__ 方法: a3 >= a2: {a3 >= a2}")  # 输出: True

# 对象相加
a4 = a1 + a3
print(f"调用 __add__ 方法: a1 + a3 = {a4}")  # 输出: A(a=3, b=5)

# 对象相减
a5 = a3 - a1
print(f"调用 __sub__ 方法: a3 - a1 = {a5}")  # 输出: A(a=1, b=1)

# 调用对象
print(f"调用 __call__ 方法: a1(4, 5) = {a1(4, 5)}")  # 输出: Called with args: (4, 5) and kwargs: {}

# 打印对象长度
print(f"调用 __len__ 方法: len(a1) = {len(a1)}")  # 输出: 3

# 删除对象
del a1  # 输出: Deleting instance with a=1 and b=2

print('''\n-----------------------------------☆☆☆分割线☆☆☆----------------------------------------\n''')


class Foo:
    def __init__(self, name):
        # 初始化方法，设置实例的 name 属性
        self.name = name

    def __getitem__(self, item):
        # 通过索引获取属性值，并打印出来
        # item 是索引的键，当你使用 obj[key] 这种形式时，item 就是 key
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        # 通过索引设置属性值
        # key 是索引的键，value 是要设置的值
        # 当你使用 obj[key] = value 这种形式时，key 就是索引的键，value 是要赋给该键的值
        self.__dict__[key] = value

    def __delitem__(self, key):
        # 通过 del 关键字删除属性，并打印提示信息
        # key 是要删除的索引键，当你使用 del obj[key] 这种形式时，key 就是索引的键
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)

    def __delattr__(self, item):
        # 通过 del 关键字删除属性，并打印提示信息
        # item 是要删除的属性名，当你使用 del obj.key 这种形式时，item 就是属性名
        print('del obj.key时,我执行')
        self.__dict__.pop(item)


# 创建一个 Foo 类的实例，传入 name 参数为 'lili'
f1 = Foo('lili')

# 使用索引操作设置属性
f1['age'] = 18  # 调用 __setitem__ 方法，将 'age' 属性设置为 18，key 是 'age'，value 是 18
f1['age1'] = 19  # 调用 __setitem__ 方法，将 'age1' 属性设置为 19，key 是 'age1'，value 是 19

# 使用 del 关键字删除属性
del f1.age1  # 调用 __delattr__ 方法，删除 'age1' 属性，并打印 'del obj.key时,我执行'，item 是 'age1'
del f1['age']  # 调用 __delitem__ 方法，删除 'age' 属性，并打印 'del obj[key]时,我执行'，key 是 'age'

# 使用索引操作修改属性
f1['name'] = 'alex'  # 调用 __setitem__ 方法，将 'name' 属性修改为 'alex'，key 是 'name'，value 是 'alex'

# 打印对象的 __dict__ 属性
print(f1.__dict__)  # 打印对象的 __dict__ 属性，输出 {'name': 'alex'}

# 在看一下__new__

'''
__new__ 是 Python 中一个非常重要的特殊方法，用于创建并返回一个新的实例。与 __init__ 不同，__new__ 是在实例创建之前被调用的，因此它负责实际的实例化过程。
理解 __new__ 的作用和使用场景对于深入掌握 Python 面向对象编程非常有帮助。

__new__ 方法的基本概念
    作用：__new__ 方法用于控制对象的创建过程，它在一个对象实例化之前被调用。
    参数：__new__(cls, *args, **kwargs)，其中 cls 是当前类本身，后面的参数是传递给 __init__ 的参数。
    返回值：__new__ 必须返回一个实例对象（通常是通过 super().__new__(cls) 创建的实例）。
__new__ 与 __init__ 的区别
    __new__：负责创建并返回一个新的实例。它是一个类方法，第一个参数是类本身。
    __init__：负责初始化已经创建的实例。它是一个实例方法，第一个参数是实例本身。
使用场景
    单例模式：确保一个类只有一个实例。
    元类编程：控制类的创建过程。
    不可变对象：如 int、str 等内置类型的子类，确保对象在创建后不可变。

'''


class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            # 调用父类的 __new__ 方法，创建一个新实例
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, value):
        self.value = value


# 创建两个实例
s1 = Singleton(42)
s2 = Singleton(99)

# 检查两个实例是否相同
print(s1 is s2)  # 输出: True

# 检查实例的值
print(s1.value)  # 输出: 99
print(s2.value)  # 输出: 99

# todo 三.单例模式

'''
【单例模式具体分析】
   
     单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例类的特殊类。
     通过单例模式可以保证系统中一个类只有一个实例而且该实例易于外界访问，从而方便对实例个数的控制并节约系统资源。
     如果希望在系统中某个类的对象只能存在一个，单例模式是最好的解决方案。

【采用单例模式动机、原因】 
    
    对于系统中的某些类来说，只有一个实例很重要，例如，一个系统中可以存在多个打印任务，但是只能有一个正在工作的任务；
一个系统只能有一个窗口管理器或文件系统；一个系统只能有一个计时工具或ID(序号)生成器。如在Windows中就只能打开一个任务管理器。
如果不使用机制对窗口对象进行唯一化，将弹出多个窗口，如果这些窗口显示的内容完全一致，
则是重复对象，浪费内存资源；如果这些窗口显示的内容不一致，则意味着在某一瞬间系统有多个状态，与实际不符，也会给用户带来误解，
不知道哪一个才是真实的状态。因此有时确保系统中某个对象的唯一性即一个类只能有一个实例非常重要。
如何保证一个类只有一个实例并且这个实例易于被访问呢？定义一个全局变量可以确保对象随时都可以被访问，
但不能防止我们实例化多个对象。一个更好的解决办法是让类自身负责保存它的唯一实例。
这个类可以保证没有其他实例被创建，并且它可以提供一个访问该实例的方法。这就是单例模式的模式动机。

【单例模式优缺点】 
    
    【优点】 
        一、实例控制 单例模式会阻止其他对象实例化其自己的单例对象的副本，从而确保所有对象都访问唯一实例。
        二、灵活性 因为类控制了实例化过程，所以类可以灵活更改实例化过程。
    【缺点】
        一、开销 虽然数量很少，但如果每次对象请求引用时都要检查是否存在类的实例，将仍然需要一些开销。可以通过使用静态初始化解决此问题。
        二、可能的开发混淆 使用单例对象（尤其在类库中定义的对象）时，开发人员必须记住自己不能使用new关键字实例化对象。因为可能无法访问库源代码，因此应用程序开发人员可能会意外发现自己无法直接实例化此类。
        三、对象生存期 不能解决删除单个对象的问题。在提供内存管理的语言中（例如基于.NET Framework的语言），只有单例类能够导致实例被取消分配，因为它包含对该实例的私有引用。在某些语言中（如 C++），其他类可以删除对象实例，但这样会导致单例类中出现悬浮引用


下面我从 Python、Java 和 Scala 这三种语言的角度来详细讨论单例模式的分类和创建。

    单例模式的分类
    饿汉式单例（Eager Initialization）
    在类加载时就创建实例，线程安全，但可能会造成资源浪费。

    懒汉式单例（Lazy Initialization）
    在首次使用时创建实例，懒加载，线程不安全，需要添加同步锁。

    双重检查锁单例（Double-Checked Locking）
    在首次使用时创建实例，使用双重检查锁定来提高性能。

    静态内部类单例（Static Inner Class）
    利用静态内部类来创建实例，线程安全，懒加载。

    枚举单例（Enum Singleton）
    利用枚举来创建单例，线程安全，防止反射和序列化攻击。

    Python
    饿汉式单例
    python
    Copy code
    class Singleton:
        instance = Singleton()

        def __init__(self):
            if hasattr(self, '_initialized'):
                return
            self._initialized = True
    懒汉式单例
    python
    Copy code
    class Singleton:
        _instance = None

        def __new__(cls):
            if cls._instance is None:
                cls._instance = super().__new__(cls)
            return cls._instance
    双重检查锁单例
    python
    Copy code
    import threading

    class Singleton:
        _instance = None
        _lock = threading.Lock()

        def __new__(cls):
            if cls._instance is None:
                with cls._lock:
                    if cls._instance is None:
                        cls._instance = super().__new__(cls)
            return cls._instance
    Java
    饿汉式单例
    java
    Copy code
    public class Singleton {
        private static final Singleton instance = new Singleton();

        private Singleton() {}

        public static Singleton getInstance() {
            return instance;
        }
    }
    懒汉式单例
    java
    Copy code
    public class Singleton {
        private static Singleton instance;

        private Singleton() {}

        public static synchronized Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    双重检查锁单例
    java
    Copy code
    public class Singleton {
        private volatile static Singleton instance;

        private Singleton() {}

        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    Scala
    饿汉式单例
    scala
    Copy code
    object Singleton {
      val instance = new Singleton
    }

    class Singleton private() {
      // 实现
    }
    懒汉式单例
    scala
    Copy code
    object Singleton {
      private var instance: Singleton = _

      def getInstance: Singleton = {
        if (instance == null) {
          synchronized {
            if (instance == null) {
              instance = new Singleton
            }
          }
        }
        instance
      }
    }

    class Singleton private() {
      // 实现
    }
    枚举单例
    scala
    Copy code
    object Singleton extends Enumeration {
      type Singleton = Value
      val instance = Value
    }
    总结
    饿汉式单例：适用于资源消耗小，保证实例唯一性的情况。
    懒汉式单例：适用于资源消耗大，需要延迟加载的情况。
    双重检查锁单例：适用于高并发环境，保证实例唯一性和性能的情况。
    静态内部类单例：适用于资源消耗小，保证实例唯一性，使用懒加载的情况。
    枚举单例：适用于防止反射和序列化攻击，保证实例唯一性的情况。
'''


# __new__创建饿汉式单例
class Singleton:
    # 在类加载时就创建实例
    __instance = object.__new__(cls)  # 直接创建实例

    def __new__(cls, *args, **kwargs):
        return cls.__instance  # 返回已创建的实例

    def __init__(self, *args, **kwargs):
        # 初始化代码
        pass


# __new__懒汉式单例

class A:
    __instance = None  # 私有类变量，用于存储单例实例

    def __new__(cls, *args, **kwargs):
        # 检查是否已经创建了实例
        if cls.__instance is None:
            # 如果尚未创建实例，调用父类的 __new__ 方法创建实例
            obj = super().__new__(cls)
            # 将新创建的实例赋值给类变量 __instance
            cls.__instance = obj
        # 返回单例实例
        return cls.__instance

    def __init__(self, *args, **kwargs):
        # 初始化代码（注意：单例模式下可能需要考虑避免重复初始化）
        pass
